package cn.okcode.slowjson.parser;

import cn.okcode.slowjson.ast.*;
import cn.okcode.slowjson.internal.BeanUtils;
import cn.okcode.slowjson.internal.JsonException;
import cn.okcode.slowjson.internal.TypeReference;
import cn.okcode.slowjson.internal.TypeUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Stack;

/**
 * 反序列化普通对象，即任意支持的对象
 * <p>
 * JsonNodeReader是更加简单的实现，这里有点搞复杂了--2021-10-16
 *
 * @param <T> 对象类型
 * @author pyk13
 */
public final class ObjectDeSerializeVisitorImpl<T> implements Visitor {

    /**
     * 用来记录类型和类型实例化的对象
     * <p>
     * 1.获取值之前pop得到当前需要解析的类型，并根据该类型创建值的实例对象
     * 2.解析处理完成，将值设置到实例对象
     * 3.将已经赋值的实例对象放入TypeHolder.push到栈
     * 4.返回上一层调用，pop出子节点的处理结果
     */
    private final Stack<TypeHolder> typeHolderStack;

    public ObjectDeSerializeVisitorImpl(Class<T> clazz) {
        this.typeHolderStack = new Stack<>();
        typeHolderStack.push(new TypeHolder(clazz));
    }

    public ObjectDeSerializeVisitorImpl(TypeReference<T> typeReference) {
        this.typeHolderStack = new Stack<>();
        typeHolderStack.push(new TypeHolder(typeReference));
    }

    public ObjectDeSerializeVisitorImpl(Class<List> clazz, Class<T> componentType) {
        this.typeHolderStack = new Stack<>();
        TypeHolder holder = new TypeHolder(clazz);
        holder.componentType = componentType;
        typeHolderStack.push(holder);
    }

    /**
     * 遍历JSON数据（包含集合）
     * <p>
     * 多级数组
     * 泛型
     *
     * @param node 节点
     */
    @Override
    public void visit(ArrayNode node) {
        TypeHolder arrayHolder = typeHolderStack.peek();

        if (!arrayHolder.isArrayOrCollection()) {
            throw new JsonException("json数组只支持转换为数组或者Collection");
        }
        Type componentType = arrayHolder.getComponentType();
        if (componentType == null) {
            throw new JsonException("没有找到数组元素的类型，数组的类型：" + arrayHolder.type.getTypeName());
        }

        @SuppressWarnings("unchecked")
        Collection<Object> collection = (Collection<Object>) TypeUtils.newInstance(Collection.class);

        for (AbstractNode<?> childNode : node.getElements()) {
            TypeHolder componentHolder = new TypeHolder(componentType);

            typeHolderStack.push(componentHolder);

            childNode.accept(this);

            componentHolder = typeHolderStack.pop();

            collection.add(componentHolder.value);
        }

        if (arrayHolder.isArray()) {
            Object arrayObject = TypeUtils.newArrayInstance(componentType, collection.size());

            BeanUtils.copyCollection(collection, arrayObject);

            arrayHolder.value = arrayObject;
        } else {
            arrayHolder.value = collection;
        }
    }

    @Override
    public void visit(ObjectNode node) {
        TypeHolder objectHolder = typeHolderStack.peek();

        Class<?> rawType = TypeUtils.getRawType(objectHolder.type);

        if (Map.class.isAssignableFrom(rawType)) {
            //Map对象
            doVisitMap(node);
        } else {
            //其它对象
            doVisitObject(node);
        }
    }

    /**
     * 将json对象节点转换成Bean
     *
     * @param node Json对象节点
     */
    private void doVisitObject(ObjectNode node) {
        TypeHolder objectHolder = typeHolderStack.peek();

        Object object = TypeUtils.newInstance(objectHolder.type);
        Class<?> rawType = TypeUtils.getRawType(objectHolder.type);

        List<Field> fields = TypeUtils.getFields(rawType);
        for (Field field : fields) {
            String property = field.getName();
            AbstractNode<?> propNode = node.getPropertyValueNode(property);
            if (propNode == null) {
                //没有从json字符串中找到字段
                continue;
            }

            TypeHolder propHolder = new TypeHolder(field.getGenericType());
            typeHolderStack.push(propHolder);

            propNode.accept(this);

            propHolder = typeHolderStack.pop();
            //设置获取到的值
            BeanUtils.setValue(object, field, propHolder.value);
        }

        objectHolder.value = object;
    }

    /**
     * 将ObjectNode节点转换为Map对象
     *
     * @param node Json对象节点
     */
    private void doVisitMap(ObjectNode node) {
        TypeHolder objectHolder = typeHolderStack.peek();

        Class<?> rawType = TypeUtils.getRawType(objectHolder.type);
        if (!Map.class.isAssignableFrom(rawType)) {
            throw new JsonException("期望的类型是Map,实际是:" + objectHolder.type.getTypeName());
        }

        Type valueType = TypeUtils.getGenericTypeArgument(objectHolder.type, 1);

        //用来临时存放值的对象
        @SuppressWarnings("unchecked")
        Map<String, Object> object = (Map<String, Object>) TypeUtils.newInstance(objectHolder.type);

        for (Map.Entry<PropertyNode, AbstractNode<?>> entry : node.getProperties().entrySet()) {
            String key = entry.getKey().getValue();

            TypeHolder propHolder = new TypeHolder(valueType);
            typeHolderStack.push(propHolder);

            AbstractNode<?> propNode = entry.getValue();

            propNode.accept(this);

            propHolder = typeHolderStack.pop();
            //设置获取到的值
            object.put(key, propHolder.value);
        }

        objectHolder.value = object;
    }

    private void doSetValue(AbstractNode<?> node) {
        TypeHolder holder = typeHolderStack.peek();
        holder.value = node.getValue();
    }

    @Override
    public void visit(BoolNode node) {
        doSetValue(node);
    }

    @Override
    public void visit(NullNode node) {
        doSetValue(node);
    }

    @Override
    public void visit(NumberNode node) {
        doSetValue(node);
    }

    @Override
    public void visit(StringNode node) {
        doSetValue(node);
    }

    @Override
    public void visit(PropertyNode node) {
        //什么都不做
    }

    @SuppressWarnings("unchecked")
    public T getBean() {
        TypeHolder holder = typeHolderStack.peek();
        return (T) holder.value;
    }

    @SuppressWarnings("unchecked")
    public List<T> getBeanList() {
        TypeHolder holder = typeHolderStack.peek();
        return (List<T>) holder.value;
    }

    /**
     * 保存Json节点对应的类型信息
     */
    private static class TypeHolder {
        /**
         * 当前节点的数据类型
         */
        private final Type type;

        /**
         * 当前节点的值
         */
        private Object value;

        /**
         * 当前节点的元素类型，只有集合或者数组才有元素类型
         */
        private Type componentType;

        private TypeHolder(Type type) {
            this.type = type;
        }

        private TypeHolder(TypeReference<?> typeReference) {
            this.type = typeReference.getType();
        }

        /**
         * 获取数组元素、或者集合元素的类型
         *
         * @return 元素类型，如果不存在元素会返回null
         */
        private Type getComponentType() {
            if (componentType == null) {
                componentType = TypeUtils.getComponentType(type);
            }
            return componentType;
        }

        private boolean isArray() {
            return TypeUtils.isArray(type);
        }

        private boolean isCollection() {
            return TypeUtils.isCollection(type);
        }

        private boolean isArrayOrCollection() {
            return isCollection() || isArray();
        }
    }
}
